package viewers;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import net.miginfocom.swing.MigLayout;

import models.HoughTransformModel;

/**
 * Control panel for the hough transform program
 * @author benjamin
 	This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class ControlPanel extends JInternalFrame implements ChangeListener,ActionListener,Observer {
	private HoughTransformModel detector;
	private JLabel lblMinimumSourceIntensity = new JLabel("Minimum intensity % needed for filtered image pixels to be transformed:");
	private JLabel lblMinimumCircumference = new JLabel("% of circumference needed for classification:");
	private JLabel lblMergeDistance = new JLabel("Merge hits within a pixel distance of:");
	private JSlider sliMinimumSourceIntensity = new JSlider(0,100);
	private JSlider sliMinimumCircumference = new JSlider(0,100);
	private JSpinner spiMergeDistance = new JSpinner();
	private JLabel lblRadius = new JLabel("Radius search-space interval:");
	private JPanel pnlRadiusInterval = new JPanel();
	private JSpinner spiMinRadius = new JSpinner();
	private JLabel lblFrom = new JLabel("From");
	private JLabel lblTo = new JLabel("  to  ");
	private JLabel lblPixels = new JLabel("pixels");
	private JSpinner spiMaxRadius = new JSpinner();
	private JButton btnFindCircles = new JButton("Find Circles");
	private ProgressIndicator progressFrame;
	
	/**
	 * Sets up the sliders,spinners and labels
	 */
	private void init(){
		this.setLayout(new MigLayout());
		GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
		Dimension screenSize = new Dimension(gd.getDisplayMode().getWidth(),gd.getDisplayMode().getHeight());
		setBounds(new Rectangle(0,screenSize.width/4,2*screenSize.width/4,screenSize.height-screenSize.width/4));
		add(lblMinimumSourceIntensity);
		add(sliMinimumSourceIntensity,   "width 350:350:350, wrap");
		add(lblMinimumCircumference);
		add(sliMinimumCircumference,   "width 350:350:350, wrap");
		add(lblMergeDistance);
		add(spiMergeDistance,   "width 50:50:50, align right, wrap");
		add(lblRadius);
		pnlRadiusInterval.setBorder(BorderFactory.createLineBorder(Color.black));
		pnlRadiusInterval.add(lblFrom,	"gapleft 0");
		pnlRadiusInterval.setLayout(new MigLayout());
		pnlRadiusInterval.add(spiMinRadius, "width 50:50:50");
		pnlRadiusInterval.add(lblTo);
		pnlRadiusInterval.add(spiMaxRadius, "width 50:50:50");
		pnlRadiusInterval.add(lblPixels);
		add(pnlRadiusInterval,	"wrap, align right");
		add(btnFindCircles,		"span, align right");
		sliMinimumCircumference.setMinimumSize(new Dimension(150,70));
		sliMinimumSourceIntensity.setValue((int)(detector.getMinimumSourcePixelIntensityPercentageThreshold()*100));
		sliMinimumSourceIntensity.setMajorTickSpacing(10);
		sliMinimumSourceIntensity.setMinimum(0);
		sliMinimumSourceIntensity.setMaximum(100);
		sliMinimumSourceIntensity.setPaintLabels(true);
		sliMinimumCircumference.setValue((int)(detector.getPercentageOfCircumferenceForDetectionThreshold()*100));
		sliMinimumCircumference.setPaintLabels(true);
		sliMinimumSourceIntensity.setPaintTicks(true);
		sliMinimumCircumference.setPaintTicks(true);
		sliMinimumCircumference.setMajorTickSpacing(10);
		sliMinimumCircumference.setMinimum(0);
		sliMinimumCircumference.setMaximum(100);
		spiMergeDistance.setValue(detector.getResultProximityMergeThreshold());
		spiMergeDistance.setModel(new SpinnerNumberModel(detector.getResultProximityMergeThreshold(),0,20,1));
		spiMinRadius.setValue(detector.getMinDetectionRadius());
		spiMinRadius.addChangeListener(this);
		spiMaxRadius.setValue(detector.getMaxDetectionRadius());
		spiMaxRadius.addChangeListener(this);
		btnFindCircles.addActionListener(this);
		pack();
		setVisible(true);
		try {
            setSelected(true);
        } catch (java.beans.PropertyVetoException e) {}
	}
	
	/**
	 * (See JInternalFrame)
	 * @param detector
	 */
	public ControlPanel(HoughTransformModel detector) {
		super();
		this.detector = detector;
		init();
	}
	/**
	 * (See JInternalFrame)
	 * @param detector
	 */
	public ControlPanel(String title, boolean resizable, boolean closable,
			boolean maximizable, boolean iconifiable, HoughTransformModel detector) {
		super(title, resizable, closable, maximizable, iconifiable);
		this.detector = detector;
		init();
	}
	/**
	 * (See JInternalFrame)
	 * @param detector
	 */
	public ControlPanel(String title, boolean resizable, boolean closable,
			boolean maximizable, HoughTransformModel detector) {
		super(title, resizable, closable, maximizable);
		this.detector = detector;
		init();
	}
	/**
	 * (See JInternalFrame)
	 * @param detector
	 */
	public ControlPanel(String title, boolean resizable, boolean closable, HoughTransformModel detector) {
		super(title, resizable, closable);
		this.detector = detector;
		init();
	}
	/**
	 * (See JInternalFrame)
	 * @param detector
	 */
	public ControlPanel(String title, boolean resizable, HoughTransformModel detector) {
		super(title, resizable);
		this.detector = detector;
		init();
	}
	/**
	 * (See JInternalFrame)
	 * @param detector
	 */
	public ControlPanel(String title, HoughTransformModel detector) {
		super(title);
		this.detector = detector;
		init();
	}

	/* monitors the spinners
	 * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
	 */
	@Override
	public void stateChanged(ChangeEvent arg0) {
		// TODO Auto-generated method stub
		if (arg0.getSource() == spiMinRadius){
			if (!spiMinRadius.getValue().toString().matches("[0-9]+"))
				spiMinRadius.setValue(0);
			else if (Integer.parseInt(spiMinRadius.getValue().toString()) < 0)
				spiMinRadius.setValue(0);
			else if (Integer.parseInt(spiMinRadius.getValue().toString()) > Integer.parseInt(spiMaxRadius.getValue().toString()))
				spiMinRadius.setValue(Integer.parseInt(spiMaxRadius.getValue().toString()));
		} else if (arg0.getSource() == spiMaxRadius){
			if (!spiMaxRadius.getValue().toString().matches("[0-9]*"))
				spiMaxRadius.setValue(0);
			else if (Integer.parseInt(spiMaxRadius.getValue().toString()) < 0)
				spiMaxRadius.setValue(0);
			else if (Integer.parseInt(spiMaxRadius.getValue().toString()) < Integer.parseInt(spiMinRadius.getValue().toString()))
				spiMaxRadius.setValue(Integer.parseInt(spiMinRadius.getValue().toString()));
		}
	}

	/* fires only when the detection button is clicked. Also creates the progress bar indicator window at this point.
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		if (e.getSource() == btnFindCircles){
			progressFrame = new ProgressIndicator(null,"Hough Transform Progress");
			detector.registerObserver(progressFrame);
			final ControlPanel me = this;
			Runnable work = new Runnable() {
	            public void run() {
	            	detector.setMaxDetectionRadius(Integer.parseInt(spiMaxRadius.getValue().toString()));
	    			detector.setMinDetectionRadius(Integer.parseInt(spiMinRadius.getValue().toString()));
	    			detector.setMinimumSourcePixelIntensityPercentageThreshold(sliMinimumSourceIntensity.getValue()/(float)100);
	    			detector.setPercentageOfCircumferenceForDetectionThreshold(sliMinimumCircumference.getValue()/(float)100);
	    			detector.setResultProximityMergeThreshold(Float.parseFloat(spiMergeDistance.getValue().toString()));
	    			btnFindCircles.setEnabled(false);
	    			try{
	    				detector.detectCircles();
	    			} catch (Exception e){
	    				JOptionPane.showMessageDialog(me,
							    e.getMessage()+'\n'+e.getStackTrace().toString(),
							    "Unexpected Error",
							    JOptionPane.ERROR_MESSAGE);
	    				e.printStackTrace();
	    				btnFindCircles.setEnabled(true);
	    			}
	            }
			};
			new Thread(work).start();
		}
	}
	/* Handler for the event-driven MVC pattern used in the program
	 * @see viewers.Observer#onObservation(java.lang.String, java.lang.Object)
	 */
	@Override
	public void onObservation(String id, Object value) {
		if (id.equals("DETECTCOMPL")){
			btnFindCircles.setEnabled(true);
			javax.swing.SwingUtilities.invokeLater(new Runnable() {
	            public void run() {
	            	detector.deregisterObserver(progressFrame);
	            	progressFrame.dispose();
	            }
			});
		}
	}
}
